#include "strategy.h"

#include <QMap>
#include <functional>


Cards Strategy::makeStrategy()
{
    Player *pendplayer = m_player->getpendplayer();
    Cards pendCards = m_player->getpendCards();

    if(pendplayer == nullptr || pendplayer == m_player)
    {
        return firstPlay();
    }
    else
    {
        PlayHand type(pendCards);
        Cards beatCards = getGreaterCards(type);
        bool shouldHand = whetherToBeat(beatCards);
        if(shouldHand)
        {
            return beatCards;
        }
        else
        {
            return Cards();
        }
    }
    return Cards();
}
Cards Strategy::firstPlay()
{
    PlayHand hand(m_cards);
    if(hand.getHandType() != PlayHand::Hand_Unknown)
    {
        return m_cards;
    }
    QVector<Cards> optimal = pickOptimalSeqSingles();
    if(!optimal.isEmpty())
    {
        int num = findCardsBycount(1).size();
        Cards save = m_cards;
        save.remove(optimal);
        int lastNum = Strategy(m_player,save).findCardsBycount(1).size();
        if(num>lastNum)
        {
            return optimal[0];
        }
    }
    bool hasPair,hasPlane,hasTriple;
	hasPair = hasTriple = hasPlane = false;
    Cards backup = m_cards;

    QVector<Cards> bombArray = findCardType(PlayHand(PlayHand::Hand_Bomb,Card::Card_Begin,0),false);
    backup.remove(bombArray);

    QVector<Cards> PlaneArray = Strategy(m_player,backup).findCardType(PlayHand(PlayHand::Hand_Plane,Card::Card_Begin,0),false);
    if(!PlaneArray.isEmpty())
    {
        hasPlane = true;
        backup.remove(PlaneArray);
    }

    QVector<Cards> TripleArray = Strategy(m_player,backup).findCardType(PlayHand(PlayHand::Hand_Triple,Card::Card_Begin,0),false);
    if(!TripleArray.isEmpty())
    {
        hasTriple = true;
        backup.remove(TripleArray);
    }

    QVector<Cards> seqPairArray = Strategy(m_player,backup).findCardType(PlayHand(PlayHand::Hand_Seq_Pair,Card::Card_Begin,0),false);
    if(!seqPairArray.isEmpty())
    {
        hasPair = true;
        backup.remove(seqPairArray);
    }
    if(hasPair)
    {
        Cards maxPair;
        for(int i = 0 ;i<seqPairArray.size();++i)
        {
            if(seqPairArray[i].cardCount()>maxPair.cardCount())
            {
                maxPair = seqPairArray[i];
            }
        }
        return maxPair;
    }

    if(hasPlane)
    {
        bool twoPairfond = false;
        QVector<Cards> PairArray;
        for(Card::CardPoint point = Card::Card_3;point <=Card::Card_10;point = Card::CardPoint(point+1))
        {
            Cards Pair = Strategy(m_player,backup).findSamePointCards(point,2);
            if(!Pair.isEmpty())
            {
                PairArray.push_back(Pair);
                if(PairArray.size() == 2)
                {
                    twoPairfond = true;
                    break;
                }
            }
        }
        if(twoPairfond)
        {
            Cards tmp = PlaneArray[0];
            tmp.add(PairArray);
            return tmp;
        }
        else
        {
            bool twoSinglefond = false;
            QVector<Cards> SingleArray;
            for(Card::CardPoint point = Card::Card_3;point <=Card::Card_10;point = Card::CardPoint(point+1))
            {
                if(backup.pointcount(point)==1)
                {
                    Cards Single = Strategy(m_player,backup).findSamePointCards(point,1);
                    if(!Single.isEmpty())
                    {
                        SingleArray.push_back(Single);
                        if(SingleArray.size() == 2)
                        {
                            twoSinglefond = true;
                            break;
                        }
                    }
                }
            }
            if(twoSinglefond)
            {
                Cards tmp = PlaneArray[0];
                tmp.add(SingleArray);
                return tmp;
            }
            else
            {
                return PlaneArray[0];
            }
        }
    }
    if(hasTriple)
    {
        if(PlayHand(TripleArray[0]).getpoint()<Card::Card_A)
        {
            for(Card::CardPoint point = Card::Card_3;point <=Card::Card_A;point = Card::CardPoint(point+1))
            {
                int pointcount = backup.pointcount(point);
                if(pointcount == 1)
                {
                    Cards single = Strategy(m_player,backup).findSamePointCards(point,1);
                    Cards temp = TripleArray[0];
                    temp.add(single);
                    return temp;
                }
                else if(pointcount == 2)
                {
                    Cards Pair = Strategy(m_player,backup).findSamePointCards(point,2    );
                    Cards temp = TripleArray[0];
                    temp.add(Pair);
                    return temp;
                }
            }
        }
        return TripleArray[0];
    }
    Player *nextPlayer = m_player->getnextPlayer();
    if(nextPlayer->getCards().cardCount() == 1 && nextPlayer->getRole() !=m_player->getRole())
    {
        for(Card::CardPoint point= Card::CardPoint(Card::Card_End-1);point>=Card::Card_3;point = Card::CardPoint(point-1))
        {
            int pointcount = backup.pointcount(point);
            if(pointcount == 1)
            {
                Cards Single = Strategy(m_player,backup).findSamePointCards(point,1);
                return Single;
            }
            else if(pointcount == 2)
            {
                Cards Pair = Strategy(m_player,backup).findSamePointCards(point,2);
                return Pair;
            }
        }
    }
    else
    {
        for(Card::CardPoint point= Card::Card_3;point<Card::Card_End;point = Card::CardPoint(point+1))
        {
            int pointcount = backup.pointcount(point);
            if(pointcount == 1)
            {
                Cards Single = Strategy(m_player,backup).findSamePointCards(point,1);
                return Single;
            }
            else if(pointcount == 2)
            {
                Cards Pair = Strategy(m_player,backup).findSamePointCards(point,2);
                return Pair;
            }
        }
    }
    return Cards();

}

Cards Strategy::getGreaterCards(PlayHand type)
{
    Player *pendPlayer = m_player->getpendplayer();
    if(pendPlayer != nullptr && pendPlayer->getRole() != m_player->getRole() && pendPlayer->getCards().cardCount()<=3)
    {
        QVector<Cards> bombs = findCardsBycount(4);
        for(int i = 0 ;i<bombs.size();++i)
        {
            if(PlayHand(bombs[i]).canBeat(type))
            {
                return bombs[i];
            }
        }
        Cards sj = findSamePointCards(Card::Card_SJ,1);
        Cards bj = findSamePointCards(Card::Card_BJ,1);
        if(!sj.isEmpty() && !bj.isEmpty())
        {
            Cards joker;
            joker<<sj<<bj;
            return joker;
        }
    }
    Player *nextPlayer = m_player->getnextPlayer();
    Cards remain = m_cards;
    remain.remove(Strategy(m_player,remain).pickOptimalSeqSingles());


    auto beatCard = std::bind([=](Cards & cards){
        QVector<Cards> beatCardsArray = Strategy(m_player, cards).findCardType(type, true);
        if(!beatCardsArray.isEmpty())
        {
            if(m_player->getRole() != nextPlayer->getRole() && nextPlayer->getCards().cardCount() <= 2)
            {
                return beatCardsArray.back();
            }
            else
            {
                return beatCardsArray.front();
            }
        }
        return Cards();
    }, std::placeholders::_1);

    Cards cs;
    if(!(cs = beatCard(remain)).isEmpty())
    {
        return cs;
    }
    else
    {
        if(!(cs = beatCard(m_cards)).isEmpty()) return cs;
    }
    return Cards();
}

bool Strategy::whetherToBeat(Cards &cards)
{
    if(cards.isEmpty())
    {
        return false;
    }
    Player *pendPlayer = m_player->getpendplayer();
    if(pendPlayer->getRole() == m_player->getRole())
    {
        Cards left = m_cards;
        left.remove(cards);
        if(PlayHand(left).getHandType()!=PlayHand::Hand_Unknown)
        {
            return true;
        }
        Card::CardPoint basePoint = PlayHand(cards).getpoint();
        if(basePoint == Card::Card_2 || basePoint == Card::Card_SJ || basePoint == Card::Card_BJ)
        {
            return false;
        }
    }
    else
    {
        PlayHand myHand(cards);
        if((myHand.getHandType() == PlayHand::Hand_Triple_Pair || myHand.getHandType() == PlayHand::Hand_Triple_Single)&&
            myHand.getpoint() == Card::Card_2)
        {
            return false;
        }
        if(myHand.getHandType()==PlayHand::Hand_Pair && myHand.getpoint() == Card::Card_2
            && pendPlayer->getCards().cardCount()>=10 && m_player->getCards().cardCount()>=5)
        {
            return false;
        }
    }
    return true;
}


Strategy::Strategy(Player *player, Cards &cards)
{
    m_player = player;
    m_cards = cards;
}

Strategy::Strategy()
{

}

Cards Strategy::findSamePointCards(Card::CardPoint point, int count)
{
    if((count<1) || (count>4))
    {
        return Cards();
    }
    if(point == Card::Card_SJ || point == Card::Card_BJ)
    {
        if(count > 1)
        {
            return Cards();
        }
        Card card;
        card.setPoint(point);
        card.setSuit(Card::Suit_Begin);
        if(m_cards.contains(card))
        {
            Cards cards;
            cards.add(card);
            return cards;
        }
        return Cards();
    }
    int findcount = 0;
    Cards findCards;
    for(int suit = Card::Suit_Begin+1;suit<Card::Suit_End;suit++)
    {
        Card card;
        card.setPoint(point);
        card.setSuit((Card::CardSuit)suit);
        if(m_cards.contains(card))
        {
            findcount++;
            findCards.add(card) ;
            if(findcount == count)
            {
                return findCards;
            }
        }
    }
    return Cards();
}

QVector<Cards> Strategy::findCardsBycount(int count)
{
    if(count<1 ||(count>4))
    {
        return QVector<Cards>();
    }

    QVector<Cards> cardArray;
    for(Card::CardPoint point = Card::Card_3;point<Card::Card_End;point = (Card::CardPoint)(point+1))
    {
        if(m_cards.pointcount(point)==count)
        {
            Cards cards;
            cards<<findSamePointCards(point,count);
            cardArray<<cards;
        }
    }
    return cardArray;
}

Cards Strategy::getRangeCards(Card::CardPoint begin, Card::CardPoint end)
{
    Cards rangecards;
    for(Card::CardPoint point = begin;point<end;point = (Card::CardPoint)(point+1))
    {
        int count = m_cards.pointcount(point);
        Cards cs = findSamePointCards(point,count);
        rangecards<<cs;
    }
    return rangecards;
}

QVector<Cards> Strategy::findCardType(PlayHand playhand, bool beat)
{
    PlayHand::HandType type = playhand.getHandType();
    Card::CardPoint point = playhand.getpoint();
    int extra = playhand.getExtra();

    Card::CardPoint beginCard = beat ? (Card::CardPoint)(point+1) : Card::Card_3;

    switch (type) {
    case PlayHand::Hand_Single:
        return getCards(beginCard,1);
    case PlayHand::Hand_Pair:
        return getCards(beginCard,2);
    case PlayHand::Hand_Triple:
        return getCards(beginCard,3);
    case PlayHand::Hand_Triple_Single:
        return getTripleSingleOrPair(beginCard,PlayHand::Hand_Single);
    case PlayHand::Hand_Triple_Pair:
        return getTripleSingleOrPair(beginCard,PlayHand::Hand_Pair);
    case PlayHand::Hand_Seq_Single:
    {
        CardInfo info;
        info.begin = beginCard;
        info.end = Card::Card_10;
        info.beat = beat;
        info.extra = extra;
        info.number = 1;
        info.base = 5;
        info.getSeq = &Strategy::getBaseSeqSingle;
        return getSeqPairOrSeqSingle(info);
    }
    case PlayHand::Hand_Seq_Pair:
    {
        CardInfo info;
        info.begin = beginCard;
        info.end = Card::Card_Q;
        info.beat = beat;
        info.number = 2;
        info.extra = extra;
        info.base = 3;
        info.getSeq = &Strategy::getBaseSeqPair;
        return getSeqPairOrSeqSingle(info);
    }
    case PlayHand::Hand_Plane:
        return getplane(beginCard);
    case PlayHand::Hand_Plane_Two_Single:
        return getplane2SingleOr2Pair(beginCard,PlayHand::Hand_Single);
    case PlayHand::Hand_Plane_Two_Pair:
        return getplane2SingleOr2Pair(beginCard,PlayHand::Hand_Pair);
    case PlayHand::Hand_Bomb:
        return getBomb(beginCard);
    default:
        return QVector<Cards>();
    }
}

void Strategy::pickSeqSingles(QVector<QVector<Cards>> &allSeqRecord,const QVector<Cards> &seqSingle,Cards &cards)
{
    QVector<Cards> allSeq = Strategy(m_player,cards).findCardType(PlayHand(PlayHand::Hand_Seq_Single,Card::Card_Begin,0),false);
    if(allSeq.isEmpty())
    {
        allSeqRecord<<seqSingle;
    }
    else
    {
        Cards saveCards = cards;
        for(int i = 0 ;i<allSeq.size();++i)
        {
            Cards aSceme = allSeq.at(i);

            Cards temp = saveCards;
            temp.remove(aSceme);

            QVector<Cards> seqArray = seqSingle;
            seqArray<<aSceme;
            pickSeqSingles(allSeqRecord,seqArray,temp);
        }
    }

}

QVector<Cards> Strategy::pickOptimalSeqSingles()
{
    QVector<QVector<Cards>> seqRecord;
    QVector<Cards> seqSingles;
    Cards save = m_cards;
    save.remove(findCardsBycount(4));
    save.remove(findCardsBycount(3));
    pickSeqSingles(seqRecord,seqSingles,save);
    if(seqRecord.isEmpty())
    {
        return QVector<Cards>();
    }
    QMap<int,int> seqMarks;
    for(int i = 0 ;i<seqRecord.size();++i)
    {
        Cards backupCards = m_cards;
        QVector<Cards> seqArray = seqRecord[i];
        backupCards.remove(seqArray);

        QVector<Cards> SinglesArray = Strategy(m_player,backupCards).findCardsBycount(1);
        QVector<Card> cardList;
        for(int i = 0 ;i<SinglesArray.size();++i)
        {
            cardList<<SinglesArray[i].toCardList();
        }
        int mark = 0;
        for(int i = 0 ;i<cardList.size();++i)
        {
            mark += cardList[i].getpoint()+15;

        }
        seqMarks.insert(i,mark);
    }
    int value = 0;
    int comMark = 1000;
    auto it = seqMarks.constBegin();
    for(;it!=seqMarks.constEnd();it++)
    {
        if(it.value()<comMark)
        {           
		 comMark = it.value();
		 value = it.key();
        }
    }
    return seqRecord[value];
}

QVector<Cards> Strategy::getTripleSingleOrPair(Card::CardPoint begin, PlayHand::HandType type)
{
    QVector<Cards> cardArray = getCards(begin,3);

    if(!cardArray.isEmpty())
    {
        Cards remaincard = m_cards;
        remaincard.remove(cardArray);

        Strategy st(m_player,remaincard);
        QVector<Cards> cardArratfinal = st.findCardType(PlayHand(type,Card::Card_Begin,0),false);
        if(!cardArratfinal.isEmpty())
        {
            for(int i = 0;i<cardArray.size();++i)
            {
                //Cards tmp;
                //tmp<<cardArratfinal[0]<<cardArratfinal[1];
                cardArray[i].add(cardArratfinal.at(i));
            }
        }
        else
        {
            cardArray.clear();
        }

    }
    return cardArray;
}

QVector<Cards> Strategy::getplane(Card::CardPoint begin)
{
    QVector<Cards> finalcardArray;
    for(Card::CardPoint pt = begin;pt<Card::Card_K;pt = (Card::CardPoint)(pt+1))
    {
        Cards prevCard = findSamePointCards(pt,3);
        Cards nextCard = findSamePointCards((Card::CardPoint)(pt+1),3);
        if(!prevCard.isEmpty() && !nextCard.isEmpty())
        {
            Cards pm;
            pm<<prevCard<<nextCard;
            finalcardArray<<pm;
        }
    }
    return finalcardArray;
}

QVector<Cards> Strategy::getplane2SingleOr2Pair(Card::CardPoint begin, PlayHand::HandType type)
{
    QVector<Cards> finalCardArray = getplane(begin);
    if(!finalCardArray.isEmpty())
    {
        Cards remaincard = m_cards;
        remaincard.remove(finalCardArray);

        Strategy st(m_player,remaincard);
        QVector<Cards> cardArrat = st.findCardType(PlayHand(type,Card::Card_Begin,0),false);
        if(cardArrat.size()>=2)
        {
            for(int i = 0;i<finalCardArray.size();++i)
            {
                Cards tmp;
                tmp<<cardArrat[0]<<cardArrat[1];
                finalCardArray[i].add(tmp);
            }
        }
        else
        {
            finalCardArray.clear();
        }

    }
    return finalCardArray;

}

QVector<Cards> Strategy::getSeqPairOrSeqSingle(CardInfo &info)
{
    QVector<Cards> finalCardArray;
    if(info.beat)
    {
        for(Card::CardPoint pt = info.begin ; pt<info.end;pt = (Card::CardPoint)(pt+1))
        {
            bool found = true;
            Cards seqCards;
            for(int i = 0 ;i<info.extra ; ++i)
            {
                Cards cards = findSamePointCards((Card::CardPoint)(pt+i),info.number);
                if(cards.isEmpty() || (pt+info.extra >=Card::Card_2))
                {
                    found = false;
                    seqCards.clear();
                    break;
                }
                else
                {
                    seqCards<<cards;
                }
            }
            if(found)
            {
                finalCardArray<<seqCards;
                return finalCardArray;
            }
        }

    }
    else
    {
        for(Card::CardPoint pt = info.begin;pt<info.end;pt = (Card::CardPoint)(pt+1))
        {
//            Cards cards0 = findSamePointCards(pt,2);
//            Cards cards1 = findSamePointCards((Card::CardPoint)(pt+1),2);
//            Cards cards2 = findSamePointCards((Card::CardPoint)(pt+2),2);

//            if(!cards0.isEmpty() && !cards1.isEmpty() && !cards2.isEmpty()) continue;

            Cards baseSeq = (this->*info.getSeq)(pt);
            if(baseSeq.isEmpty()) continue;
            //baseSeq<<cards0<<cards1<<cards2;
            finalCardArray << baseSeq;

            int follow = info.base;
            Cards alreadyfollowCard;
            while(true)
            {
                Card::CardPoint followPoint = (Card::CardPoint)(pt+follow);

                if(followPoint >=Card::Card_2)
                {
                    break;
                }
                Cards followCard = findSamePointCards(followPoint,info.number);
                if(followCard.isEmpty())
                {
                    break;
                }
                else
                {
                    alreadyfollowCard<<followCard;
                    Cards newseq = baseSeq;
                    newseq<<alreadyfollowCard;
                    finalCardArray<<newseq;
                    follow++;
                }
            }
        }
    }
    return finalCardArray;
}

Cards Strategy::getBaseSeqPair(Card::CardPoint point)
{
    Cards baseSeq;
    Cards card1 = findSamePointCards(point,2);
    Cards card2 = findSamePointCards((Card::CardPoint)(point+1),2);
    Cards card3 = findSamePointCards((Card::CardPoint)(point+2),2);

    if(!card1.isEmpty() && !card2.isEmpty() && !card3.isEmpty())
    {
        baseSeq<<card1<<card2<<card3;
    }
    return baseSeq;
}

Cards Strategy::getBaseSeqSingle(Card::CardPoint point)
{
    Cards baseSeq;
    Cards card1 = findSamePointCards(point,1);
    Cards card2 = findSamePointCards((Card::CardPoint)(point+1),1);
    Cards card3 = findSamePointCards((Card::CardPoint)(point+2),1);
    Cards card4 = findSamePointCards((Card::CardPoint)(point+3),1);
    Cards card5 = findSamePointCards((Card::CardPoint)(point+4),1);

    if(!card1.isEmpty() && !card2.isEmpty() && !card3.isEmpty() && !card4.isEmpty() && !card5.isEmpty())
    {
        baseSeq<<card1<<card2<<card3<<card4<<card5;
    }
    return baseSeq;
}

QVector<Cards> Strategy::getBomb(Card::CardPoint begin)
{
    QVector<Cards> finalCardArray;
    for(Card::CardPoint pt = begin;pt<Card::Card_End;pt = (Card::CardPoint)(pt+1))
    {
        Cards cards = findSamePointCards(pt,4);
        if(!cards.isEmpty())
        {
            finalCardArray<<cards;
        }
    }
    return finalCardArray;
}

QVector<Cards> Strategy::getCards(Card::CardPoint point, int numble)
{
    QVector<Cards> cardArray;
    for(Card::CardPoint pt = point;pt<Card::Card_End;pt = (Card::CardPoint)(pt+1))
    {
        if(m_cards.pointcount(pt) == numble)
        {
            Cards cards = findSamePointCards(pt,numble);
            cardArray<<cards;
        }
    }
    return cardArray;
}
